home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectInput / MultiMapper / Multidi.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  32.5 KB  |  910 lines

  1. //-----------------------------------------------------------------------------
  2. // File: MultiDI.cpp
  3. //
  4. // Desc: DirectInput framework class using semantic mapping with multiplayer
  5. //       device ownership.  Feel free to use this class as a starting point 
  6. //       for adding extra functionality.
  7. //
  8. // Copyright (C) 1995-2001 Microsoft Corporation. All Rights Reserved.
  9. //-----------------------------------------------------------------------------
  10. #define STRICT
  11. #define DIRECTINPUT_VERSION 0x0800
  12. #include <basetsd.h>
  13. #include <tchar.h>
  14. #include <stdio.h>
  15. #include <windows.h>
  16. #include <shlwapi.h>    // defines SHDeleteKey
  17. #include <dxerr8.h>
  18. #include <d3d8types.h>  // included to get the D3DCOLOR_RGBA macro.
  19. #include "MultiDI.h"
  20. #include "DXUtil.h"
  21. #include <assert.h>
  22.  
  23.  
  24.  
  25.  
  26. //-----------------------------------------------------------------------------
  27. // Name: CMultiplayerInputDeviceManager
  28. // Desc: Constructor
  29. // Args: strRegKey - A location in the registry where device ownership 
  30. //       information should be stored
  31. //-----------------------------------------------------------------------------
  32. CMultiplayerInputDeviceManager::CMultiplayerInputDeviceManager( TCHAR* strRegKey )
  33. {
  34.     HRESULT hr = CoInitialize(NULL);
  35.     m_bCleanupCOM = SUCCEEDED(hr);
  36.     LONG nResult;
  37.  
  38.     // Initialize members
  39.     m_pDI                       = NULL;
  40.     m_hWnd                      = NULL;
  41.     m_pdiaf                     = NULL;
  42.     m_pUsers                    = NULL;
  43.     m_pDeviceList               = NULL;
  44.     m_AddDeviceCallback         = NULL;
  45.     m_AddDeviceCallbackParam    = NULL;
  46.     m_hKey                      = NULL;
  47.     m_dwNumDevices              = 0;
  48.     m_dwMaxDevices              = 0; 
  49.     
  50.  
  51.     // Duplicate the registry location string since we'll need this again later
  52.     m_strKey = _tcsdup( strRegKey );
  53.     if( m_strKey == NULL )
  54.         return;
  55.  
  56.     // Create a reg key to store device ownership data 
  57.     nResult = RegCreateKeyEx( HKEY_CURRENT_USER, strRegKey, 0, NULL,
  58.                               REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, 
  59.                               &m_hKey, NULL );
  60.     if(nResult != ERROR_SUCCESS)
  61.         m_hKey = NULL;
  62. }
  63.  
  64.  
  65.  
  66.  
  67. //-----------------------------------------------------------------------------
  68. // Name: ~CMultiplayerInputDeviceManager
  69. // Desc: Destructor
  70. //-----------------------------------------------------------------------------
  71. CMultiplayerInputDeviceManager::~CMultiplayerInputDeviceManager()
  72. {
  73.     Cleanup();
  74.     
  75.     if( m_bCleanupCOM )
  76.         CoUninitialize();
  77.  
  78.     RegCloseKey( m_hKey );
  79.  
  80.     free( m_strKey );
  81. }
  82.  
  83.  
  84.  
  85.  
  86. //-----------------------------------------------------------------------------
  87. // Name: Create
  88. // Desc: Initializes the class, and enums the devices.  See MultiMapper sample
  89. //       for how to use this class.
  90. //       It might fail if there are too many players for the 
  91. //       number of devices availible, or if one player owns too many
  92. //       devices preventing others from having a device.  Its up the app
  93. //       to prevent this or respond to this.
  94. //       Note: strUserName should be a array of sz strings 
  95. //-----------------------------------------------------------------------------
  96. HRESULT CMultiplayerInputDeviceManager::Create( HWND hWnd, 
  97.                                      TCHAR* strUserNames[], 
  98.                                      DWORD dwNumUsers,
  99.                                      DIACTIONFORMAT* pdiaf,
  100.                                      LPDIMANAGERCALLBACK AddDeviceCallback, 
  101.                                      LPVOID pCallbackParam,
  102.                                      BOOL bResetOwnership, 
  103.                                      BOOL bResetMappings )
  104. {
  105.     HRESULT hr;
  106.     
  107.     if( strUserNames == NULL || dwNumUsers == 0 )
  108.         return E_INVALIDARG;
  109.  
  110.     Cleanup();
  111.     
  112.     // Store data
  113.     m_hWnd = hWnd;
  114.  
  115.     // Create and init the m_pUsers array 
  116.     m_dwNumUsers = dwNumUsers;
  117.     m_pUsers     = new PlayerInfo*[dwNumUsers];
  118.     for( DWORD i=0; i<dwNumUsers; i++ )
  119.     {
  120.         m_pUsers[i] = new PlayerInfo;
  121.         ZeroMemory( m_pUsers[i], sizeof(PlayerInfo) );        
  122.         m_pUsers[i]->dwPlayerIndex = i; // set the 0-based player index (for easy referencing)
  123.         lstrcpyn( m_pUsers[i]->strPlayerName, strUserNames[i], MAX_PATH-1 );            
  124.     }
  125.     
  126.     m_AddDeviceCallback = AddDeviceCallback;
  127.     m_AddDeviceCallbackParam = pCallbackParam;
  128.  
  129.     // Create the base DirectInput object
  130.     if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, 
  131.                                          IID_IDirectInput8, (VOID**)&m_pDI, NULL ) ) )
  132.         return DXTRACE_ERR_NOMSGBOX( TEXT("DirectInput8Create"), hr );
  133.     
  134.     if( FAILED( hr = SetActionFormat( pdiaf, TRUE, bResetOwnership, bResetMappings ) ) )
  135.         return DXTRACE_ERR_NOMSGBOX( TEXT("SetActionFormat"), hr );
  136.     
  137.     return S_OK;
  138. }
  139.  
  140.  
  141.  
  142.  
  143. //-----------------------------------------------------------------------------
  144. // Name: SetActionFormat
  145. // Desc: Sets a new action format.  
  146. //       It re-enumerates the devices if bReenumerate
  147. //       It resets the ownership of the devices if bResetOwnership
  148. //       It resets the mapper actions of the devices if bResetMappings
  149. //       This function may fail if there are too many players for the 
  150. //       number of devices availible, or if one player owns too many
  151. //       devices preventing others from having a device.  Its up the app
  152. //       to prevent this or respond to this.
  153. //-----------------------------------------------------------------------------
  154. HRESULT CMultiplayerInputDeviceManager::SetActionFormat( DIACTIONFORMAT* pdiaf, 
  155.                                               BOOL bReenumerate, 
  156.                                               BOOL bResetOwnership,
  157.                                               BOOL bResetMappings )
  158. {
  159.     HRESULT hr;
  160.     DWORD iPlayer;
  161.     DWORD iDevice;
  162.  
  163.     // Store the new action format
  164.     m_pdiaf = pdiaf;
  165.     
  166.     // Only destroy and re-enumerate devices if the caller explicitly wants to.
  167.     // This isn't thread safe, so be sure not to have any other threads using
  168.     // this data unless you redesign this class
  169.     if( bReenumerate )
  170.     {
  171.         // Set all players to not have a device yet
  172.         for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
  173.             m_pUsers[iPlayer]->bFoundDeviceForPlayer = FALSE;
  174.  
  175.         if( bResetOwnership )
  176.         {
  177.             // Set all devices as not assigned to a player
  178.             for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  179.                 m_pDeviceList[iDevice].pPlayerInfo = NULL;
  180.  
  181.             // Delete the device ownership keys
  182.             DeleteDeviceOwnershipKeys();
  183.         }
  184.  
  185.         // Devices must be unacquired to have a new action map set.
  186.         UnacquireDevices();
  187.         
  188.         // Enumerate all available devices that map the pri 1 actions,
  189.         // and store them in the m_pDeviceList array
  190.         if( FAILED( hr = BuildDeviceList() ) )
  191.             return DXTRACE_ERR_NOMSGBOX( TEXT("BuildDeviceList"), hr );
  192.         
  193.         // Assign devices to any players that don't have devices
  194.         // Some games may want to change this functionality.
  195.         if( FAILED( hr = AssignDevices() ) )
  196.             return DXTRACE_ERR_NOMSGBOX( TEXT("AssignDevices"), hr );
  197.  
  198.         // Report an error if there are too many players for the 
  199.         // number of devices availible, or if one player owns too many
  200.         // devices preventing others from having a device
  201.         if( FAILED( hr = VerifyAssignment() ) )
  202.             return hr;
  203.  
  204.         // Now that every player has at least one device, build and set 
  205.         // the action map, and use callback into the app to tell the 
  206.         // app of the device assignment.
  207.         if( FAILED( hr = AddAssignedDevices( bResetMappings ) ) )
  208.             return DXTRACE_ERR_NOMSGBOX( TEXT("AddAssignedDevices"), hr );
  209.  
  210.         // For every device that's assigned to a player, save its device key 
  211.         // and assigned player to registry, so the app remembers which devices
  212.         // each player prefers
  213.         if( FAILED( hr = SaveDeviceOwnershipKeys() ) )
  214.             return DXTRACE_ERR_NOMSGBOX( TEXT("SaveDeviceOwnershipKeys"), hr );
  215.     }
  216.     else
  217.     {
  218.         // Just apply the new maps for each device owned by each user
  219.         
  220.         // Devices must be unacquired to have a new action map set.
  221.         UnacquireDevices();
  222.         
  223.         // Apply the new action map to the current devices.
  224.         for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  225.         {
  226.             LPDIRECTINPUTDEVICE8 pdidDevice = m_pDeviceList[iDevice].pdidDevice;
  227.             PlayerInfo* pPlayerInfo = m_pDeviceList[iDevice].pPlayerInfo;
  228.  
  229.             if( FAILED( hr = pdidDevice->BuildActionMap( m_pdiaf, pPlayerInfo->strPlayerName, DIDBAM_DEFAULT ) ) )
  230.                 return DXTRACE_ERR_NOMSGBOX( TEXT("BuildActionMap"), hr );
  231.             if( FAILED( hr = pdidDevice->SetActionMap( m_pdiaf, pPlayerInfo->strPlayerName, DIDSAM_DEFAULT ) ) )
  232.                 return DXTRACE_ERR_NOMSGBOX( TEXT("SetActionMap"), hr );
  233.         }
  234.     }
  235.  
  236.     return S_OK;
  237. }
  238.  
  239.  
  240.  
  241.  
  242. //-----------------------------------------------------------------------------
  243. // Name: BuildDeviceList
  244. // Desc:
  245. //-----------------------------------------------------------------------------
  246. HRESULT CMultiplayerInputDeviceManager::BuildDeviceList()
  247. {
  248.     // Cleanup any previously enumerated devices
  249.     CleanupDeviceList();
  250.     
  251.     // Build a simple list of all devices currently attached to the machine. This
  252.     // array will be used to reassign devices to each user.
  253.     m_dwMaxDevices = 5;
  254.     m_dwNumDevices = 0;
  255.     m_pDeviceList = NULL;
  256.     m_pDeviceList = (DeviceInfo*) realloc( m_pDeviceList, m_dwMaxDevices*sizeof(DeviceInfo) );
  257.     ZeroMemory( m_pDeviceList, m_dwMaxDevices*sizeof(DeviceInfo) );        
  258.  
  259.     // Enumerate available devices for any user.
  260.     m_pDI->EnumDevicesBySemantics( NULL, m_pdiaf, StaticEnumSuitableDevicesCB, 
  261.                                    this, DIEDBSFL_ATTACHEDONLY );
  262.  
  263.     return S_OK;
  264. }
  265.  
  266.  
  267.  
  268.  
  269. //-----------------------------------------------------------------------------
  270. // Name: EnumSuitableDevicesCB
  271. // Desc: DirectInput device enumeratation callback.  Calls AddDevice()
  272. //       on each device enumerated.
  273. //-----------------------------------------------------------------------------
  274. BOOL CALLBACK CMultiplayerInputDeviceManager::StaticEnumSuitableDevicesCB( LPCDIDEVICEINSTANCE pdidi, 
  275.                                                                LPDIRECTINPUTDEVICE8 pdidDevice, 
  276.                                                                DWORD dwFlags, DWORD dwDeviceRemaining,
  277.                                                                VOID* pContext )
  278. {
  279.     // Add the device to the device manager's internal list
  280.     CMultiplayerInputDeviceManager* pInputDeviceManager = (CMultiplayerInputDeviceManager*)pContext;
  281.     return pInputDeviceManager->EnumDevice( pdidi, pdidDevice, 
  282.                                             dwFlags, dwDeviceRemaining );    
  283. }
  284.  
  285.  
  286.  
  287.  
  288. //-----------------------------------------------------------------------------
  289. // Name: EnumDevice
  290. // Desc: Enums each device to see if its suitable to add 
  291. //-----------------------------------------------------------------------------
  292. BOOL CMultiplayerInputDeviceManager::EnumDevice( const DIDEVICEINSTANCE* pdidi, 
  293.                                      const LPDIRECTINPUTDEVICE8 pdidDevice,
  294.                                      DWORD dwFlags, DWORD dwRemainingDevices )
  295. {    
  296.     TCHAR strPlayerName[MAX_PATH];
  297.     TCHAR strDeviceGuid[40];
  298.     
  299.     // Devices of type DI8DEVTYPE_DEVICECTRL are specialized devices not generally
  300.     // considered appropriate to control game actions. We just ignore these.
  301.     if( GET_DIDEVICE_TYPE(pdidi->dwDevType) != DI8DEVTYPE_DEVICECTRL )
  302.     {
  303.         // We're only interested in devices that map the pri 1 actions
  304.         if( dwFlags & DIEDBS_MAPPEDPRI1 )
  305.         {
  306.             // Add new pdidDevice struct to array, and resize array if needed
  307.             m_dwNumDevices++;
  308.             if( m_dwNumDevices > m_dwMaxDevices )
  309.             {
  310.                 m_dwMaxDevices += 5;
  311.                 m_pDeviceList = (DeviceInfo*) realloc( m_pDeviceList, m_dwMaxDevices*sizeof(DeviceInfo) );
  312.                 ZeroMemory( m_pDeviceList + m_dwMaxDevices - 5, 5*sizeof(DeviceInfo) );
  313.             }
  314.             
  315.             DXUtil_ConvertGUIDToString( &pdidi->guidInstance, strDeviceGuid );
  316.             DXUtil_ReadStringRegKey( m_hKey, strDeviceGuid, strPlayerName, MAX_PATH, TEXT("") );        
  317.             
  318.             // Add the device to the array m_pDeviceList
  319.             DWORD dwCurrentDevice = m_dwNumDevices-1;
  320.             ZeroMemory( &m_pDeviceList[dwCurrentDevice], sizeof(DeviceInfo) );
  321.  
  322.             m_pDeviceList[dwCurrentDevice].didi = *pdidi;            
  323.             m_pDeviceList[dwCurrentDevice].pdidDevice = pdidDevice;        
  324.             m_pDeviceList[dwCurrentDevice].pdidDevice->AddRef();
  325.             m_pDeviceList[dwCurrentDevice].bMapsPri1Actions = ((dwFlags & DIEDBS_MAPPEDPRI1) == DIEDBS_MAPPEDPRI1);
  326.             m_pDeviceList[dwCurrentDevice].bMapsPri2Actions = ((dwFlags & DIEDBS_MAPPEDPRI2) == DIEDBS_MAPPEDPRI2);
  327.             
  328.             if( lstrcmp( strPlayerName, TEXT("") ) != 0 )
  329.             {
  330.                 m_pDeviceList[dwCurrentDevice].pPlayerInfo = LookupPlayer( strPlayerName );
  331.                 if( m_pDeviceList[dwCurrentDevice].pPlayerInfo )
  332.                     m_pDeviceList[dwCurrentDevice].pPlayerInfo->bFoundDeviceForPlayer = TRUE;
  333.             }
  334.         }
  335.     }
  336.     
  337.     // Continue enumerating 
  338.     return DIENUM_CONTINUE;
  339. }
  340.  
  341.  
  342.  
  343.  
  344. //-----------------------------------------------------------------------------
  345. // Name: AssignDevices
  346. // Desc:
  347. //-----------------------------------------------------------------------------
  348. HRESULT CMultiplayerInputDeviceManager::AssignDevices()
  349. {    
  350.     DWORD iDevice;
  351.     DWORD iPlayer;
  352.  
  353.     // For any device that doesn't have a user assigned to it,
  354.     // then assign it to the first user that needs a device
  355.     for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  356.     {
  357.         if( m_pDeviceList[iDevice].pPlayerInfo == NULL )
  358.         {
  359.             for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
  360.             {
  361.                 if( !m_pUsers[iPlayer]->bFoundDeviceForPlayer )
  362.                 {
  363.                     m_pDeviceList[iDevice].pPlayerInfo = m_pUsers[iPlayer];
  364.                     m_pUsers[iPlayer]->bFoundDeviceForPlayer = TRUE;
  365.                     break;
  366.                 }
  367.             }                        
  368.         }
  369.     }
  370.  
  371.     return S_OK;
  372. }
  373.  
  374.  
  375.  
  376.  
  377. //-----------------------------------------------------------------------------
  378. // Name: VerifyAssignment
  379. // Desc:
  380. //-----------------------------------------------------------------------------
  381. HRESULT CMultiplayerInputDeviceManager::VerifyAssignment()
  382. {    
  383.     DWORD iPlayer;
  384.     DWORD iDevice;
  385.  
  386.     // For each player, make sure that a device was found for this 
  387.     // player, otherwise return failure.
  388.     for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
  389.     {
  390.         if( !m_pUsers[iPlayer]->bFoundDeviceForPlayer )
  391.         {
  392.             if( GetNumDevices() < m_dwNumUsers )
  393.                 return E_DIUTILERR_TOOMANYUSERS;
  394.             else
  395.             {
  396.                 // Check to see if there's a device that isn't already
  397.                 // assigned to a player. If there is return 
  398.                 // E_DIUTILERR_PLAYERWITHOUTDEVICE otherwise return 
  399.                 // E_DIUTILERR_DEVICESTAKEN
  400.                 for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  401.                 {
  402.                     if( m_pDeviceList[iDevice].pPlayerInfo == NULL )
  403.                         return E_DIUTILERR_PLAYERWITHOUTDEVICE;
  404.                 }
  405.                 
  406.                 return E_DIUTILERR_DEVICESTAKEN;
  407.             }
  408.         }                       
  409.     }
  410.  
  411.     return S_OK;
  412. }
  413.  
  414.  
  415.  
  416.  
  417. //-----------------------------------------------------------------------------
  418. // Name: AddAssignedDevices
  419. // Desc: For every device that's assigned to a player, set it action map
  420. //       and add it to the game
  421. //-----------------------------------------------------------------------------
  422. HRESULT CMultiplayerInputDeviceManager::AddAssignedDevices( BOOL bResetMappings )
  423. {    
  424.     DWORD iDevice;
  425.     DWORD iAction;
  426.     HRESULT hr = S_OK;
  427.     DWORD *pdwAppFixed = NULL;
  428.  
  429.     // If flagged, we'll be remapping the actions to hardware defaults, and in
  430.     // the process DirectInput will clear the DIA_APPFIXED flag, so we need 
  431.     // to make a copy in order to reapply the flag later.
  432.     if( bResetMappings )
  433.     {
  434.         pdwAppFixed = new DWORD[m_pdiaf->dwNumActions];
  435.         
  436.         // Verify memory allocation and collect DIA_APPFIXED settings
  437.         if( pdwAppFixed )
  438.         {
  439.             for( iAction=0; iAction < m_pdiaf->dwNumActions; iAction++ )
  440.                 pdwAppFixed[iAction] = m_pdiaf->rgoAction[iAction].dwFlags & DIA_APPFIXED;
  441.         }
  442.     }
  443.  
  444.     // For every device that's assigned to a player, 
  445.     // set it action map, and add it to the game
  446.     for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  447.     {                
  448.         LPDIRECTINPUTDEVICE8 pdidDevice = m_pDeviceList[iDevice].pdidDevice;
  449.         PlayerInfo*          pPlayerInfo = m_pDeviceList[iDevice].pPlayerInfo;           
  450.         
  451.         if( pPlayerInfo != NULL )
  452.         {   
  453.             // Set the device's coop level
  454.             hr = pdidDevice->SetCooperativeLevel( m_hWnd, DISCL_NONEXCLUSIVE|DISCL_FOREGROUND );
  455.             if( FAILED(hr) )
  456.                 break;
  457.             
  458.             // Build and set the action map on this device.  This will also remove 
  459.             // it from DirectInput's internal list of available devices.
  460.             DWORD dwBuildFlags = bResetMappings ? DIDBAM_HWDEFAULTS : DIDBAM_DEFAULT;   
  461.               DWORD dwSetFlags = bResetMappings ? DIDSAM_FORCESAVE : DIDSAM_DEFAULT;
  462.  
  463.             hr = pdidDevice->BuildActionMap( m_pdiaf, pPlayerInfo->strPlayerName, dwBuildFlags );
  464.             if( FAILED( hr ) )
  465.             {
  466.                 // just print out a debug message and keep going
  467.                 DXTRACE_ERR_NOMSGBOX( TEXT("BuildActionMap"), hr );
  468.                 hr = S_OK;
  469.                 continue;
  470.             }
  471.  
  472.             hr = pdidDevice->SetActionMap( m_pdiaf, pPlayerInfo->strPlayerName, dwSetFlags );
  473.             if( FAILED( hr ) )
  474.             {
  475.                 // just print out a debug message and keep going
  476.                 DXTRACE_ERR_NOMSGBOX( TEXT("SetActionMap"), hr );   
  477.                 hr = S_OK;
  478.                 continue;
  479.             }
  480.             
  481.  
  482.             // Callback into the app so it can adjust the device and set
  483.             // the m_pDeviceList[iDevice].pParam field with a device state struct
  484.             if( m_AddDeviceCallback )
  485.                 m_AddDeviceCallback( pPlayerInfo, &m_pDeviceList[iDevice], 
  486.                                      &m_pDeviceList[iDevice].didi, m_AddDeviceCallbackParam );            
  487.             
  488.             // Check to see if the device is using relative axis -- sometimes app code
  489.             // might want to know this.
  490.             DIPROPDWORD dipdw;
  491.             dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
  492.             dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  493.             dipdw.diph.dwObj        = 0;
  494.             dipdw.diph.dwHow        = DIPH_DEVICE;
  495.             dipdw.dwData            = 0;
  496.             pdidDevice->GetProperty( DIPROP_AXISMODE, &dipdw.diph );
  497.             if( dipdw.dwData == DIPROPAXISMODE_REL )
  498.                 m_pDeviceList[iDevice].bRelativeAxis = TRUE;   
  499.             
  500.             // We made it through this iteration without breaking out do to errors
  501.             hr = S_OK;
  502.         }
  503.         else
  504.         {
  505.             if( FAILED( hr = pdidDevice->BuildActionMap( m_pdiaf, NULL, DIDBAM_DEFAULT ) ) )
  506.             {
  507.                 DXTRACE_ERR_NOMSGBOX( TEXT("BuildActionMap"), hr );
  508.                 break;
  509.             }
  510.             
  511.             if( FAILED( hr = pdidDevice->SetActionMap( m_pdiaf, NULL, DIDSAM_NOUSER ) ) )
  512.             {
  513.                 DXTRACE_ERR_NOMSGBOX( TEXT("SetActionMap"), hr );   
  514.                 break;
  515.             }
  516.         }
  517.     }                              
  518.  
  519.     // If we stored DIA_APPFIXED flags earlier, we need to reapply those flags and
  520.     // free the allocated memory
  521.     if( bResetMappings && pdwAppFixed )
  522.     {
  523.         for( iAction=0; iAction < m_pdiaf->dwNumActions; iAction++ )
  524.             m_pdiaf->rgoAction[iAction].dwFlags |= pdwAppFixed[iAction];
  525.         
  526.         delete [] pdwAppFixed;
  527.     }
  528.  
  529.     return hr;
  530. }
  531.  
  532.  
  533.  
  534.  
  535. //-----------------------------------------------------------------------------
  536. // Name: ConfigureDevices
  537. // Desc:
  538. //-----------------------------------------------------------------------------
  539. HRESULT CMultiplayerInputDeviceManager::ConfigureDevices( HWND hWnd, IUnknown* pSurface,
  540.                                                VOID* ConfigureDevicesCB,
  541.                                                DWORD dwFlags, LPVOID pvCBParam )
  542. {
  543.     HRESULT hr;
  544.     DWORD iPlayer;
  545.     
  546.     // Determine how large of a string we'll need to hold all user names
  547.     DWORD dwNamesSize = 0;
  548.     for( iPlayer=0; iPlayer < m_dwNumUsers; iPlayer++ )
  549.         dwNamesSize += lstrlen( m_pUsers[iPlayer]->strPlayerName ) +1;
  550.  
  551.     // Build multi-sz list of user names
  552.     TCHAR* strUserNames = new TCHAR[dwNamesSize+1];
  553.     
  554.     // Verify allocation and cycle through user names
  555.     if( strUserNames ) 
  556.     {
  557.         TCHAR* strTemp = strUserNames;
  558.         for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
  559.         {
  560.             lstrcpy( strTemp, m_pUsers[iPlayer]->strPlayerName );
  561.             strTemp += lstrlen(strTemp) + 1;
  562.         }
  563.  
  564.         lstrcpy( strTemp, TEXT("\0") );
  565.     }
  566.  
  567.     
  568.     // Fill in all the params
  569.     DICONFIGUREDEVICESPARAMS dicdp;
  570.     ZeroMemory(&dicdp, sizeof(dicdp));
  571.     dicdp.dwSize = sizeof(dicdp);
  572.     dicdp.dwcFormats     = 1;
  573.     dicdp.lprgFormats    = m_pdiaf;
  574.     dicdp.hwnd           = hWnd;
  575.     dicdp.lpUnkDDSTarget = pSurface;
  576.     dicdp.dwcUsers       = m_dwNumUsers;
  577.     dicdp.lptszUserNames = strUserNames;
  578.  
  579.     // Initialize all the colors here
  580.     DICOLORSET dics;
  581.     ZeroMemory(&dics, sizeof(DICOLORSET));
  582.     dics.dwSize = sizeof(DICOLORSET);
  583.  
  584.     // Set UI color scheme (if not specified it uses defaults)
  585.     dicdp.dics.dwSize = sizeof(dics);
  586.     dicdp.dics.cTextFore        = D3DCOLOR_RGBA(255,255,255,255);
  587.     dicdp.dics.cTextHighlight   = D3DCOLOR_RGBA(60,191,241,255);
  588.     dicdp.dics.cCalloutLine     = D3DCOLOR_RGBA(255,255,255,128);
  589.     dicdp.dics.cCalloutHighlight= D3DCOLOR_RGBA(60,191,241,255);
  590.     dicdp.dics.cBorder          = D3DCOLOR_RGBA(140,152,140,128);
  591.     dicdp.dics.cControlFill     = D3DCOLOR_RGBA(113,0,0,128);
  592.     dicdp.dics.cHighlightFill   = D3DCOLOR_RGBA(0,0,0,128);
  593.     dicdp.dics.cAreaFill        = D3DCOLOR_RGBA(0,0,0,128);
  594.         
  595.     if( dwFlags & DICD_EDIT )
  596.     {
  597.         // Re-enum so we can catch any new devices that have been recently attached
  598.         for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
  599.             m_pUsers[iPlayer]->bFoundDeviceForPlayer = FALSE;
  600.         if( FAILED( hr = BuildDeviceList() ) )
  601.         {
  602.             DXTRACE_ERR_NOMSGBOX( TEXT("BuildDeviceList"), hr );
  603.             goto LCleanup;
  604.         }
  605.     }
  606.         
  607.     // Unacquire the devices so that mouse doesn't 
  608.     // control the game while in control panel
  609.     UnacquireDevices();
  610.  
  611.     if( FAILED( hr = m_pDI->ConfigureDevices( (LPDICONFIGUREDEVICESCALLBACK)ConfigureDevicesCB, 
  612.                                   &dicdp, dwFlags, pvCBParam ) ) )
  613.     {
  614.         DXTRACE_ERR_NOMSGBOX( TEXT("ConfigureDevices"), hr );   
  615.         goto LCleanup;
  616.     }
  617.  
  618.     if( dwFlags & DICD_EDIT )
  619.     {
  620.         // Update the device ownership 
  621.         if( FAILED( hr = UpdateDeviceOwnership() ) )
  622.         {
  623.             DXTRACE_ERR_NOMSGBOX( TEXT("UpdateDeviceOwnership"), hr );    
  624.             goto LCleanup;
  625.         }
  626.  
  627.         // Now save the device keys that are assigned players to registry, 
  628.         if( FAILED( hr = SaveDeviceOwnershipKeys() ) )
  629.         {
  630.             DXTRACE_ERR_NOMSGBOX( TEXT("SaveDeviceOwnershipKeys"), hr );
  631.             goto LCleanup;
  632.         }
  633.  
  634.         // Report an error if there is a player that doesn't not
  635.         // have a device assigned
  636.         if( FAILED( hr = VerifyAssignment() ) )
  637.             goto LCleanup;
  638.         
  639.         // Now that every player has at least one device, build and set 
  640.         // the action map, and use callback into the app to tell the 
  641.         // app of the device assignment.
  642.         if( FAILED( hr = AddAssignedDevices( FALSE ) ) )
  643.         {
  644.             DXTRACE_ERR_NOMSGBOX( TEXT("AddAssignedDevices"), hr );
  645.             goto LCleanup;
  646.         }
  647.     }
  648.     
  649.     hr = S_OK;
  650.  
  651. LCleanup:
  652.  
  653.     if( strUserNames )
  654.         delete [] strUserNames;
  655.  
  656.     return hr;
  657. }
  658.  
  659.  
  660.  
  661.  
  662. //-----------------------------------------------------------------------------
  663. // Name: UpdateDeviceOwnership
  664. // Desc:
  665. //-----------------------------------------------------------------------------
  666. HRESULT CMultiplayerInputDeviceManager::UpdateDeviceOwnership()
  667. {
  668.     DWORD   iPlayer;
  669.     DWORD   iDevice;
  670.  
  671.     // Set all players to not have a device yet
  672.     for( iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
  673.         m_pUsers[iPlayer]->bFoundDeviceForPlayer = FALSE;
  674.  
  675.     // Set all devices as not assigned to a player
  676.     for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  677.         m_pDeviceList[iDevice].pPlayerInfo = NULL;
  678.  
  679.     UnacquireDevices();
  680.     
  681.     // Update the device ownership by quering the DIPROP_USERNAME property
  682.     for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  683.     {
  684.         LPDIRECTINPUTDEVICE8 pdidDevice = m_pDeviceList[iDevice].pdidDevice;
  685.  
  686.         TCHAR strPlayerName[MAX_PATH];
  687.         DIPROPSTRING dips;
  688.         dips.diph.dwSize       = sizeof(DIPROPSTRING); 
  689.         dips.diph.dwHeaderSize = sizeof(DIPROPHEADER); 
  690.         dips.diph.dwObj        = 0; // device property 
  691.         dips.diph.dwHow        = DIPH_DEVICE;                     
  692.         pdidDevice->GetProperty( DIPROP_USERNAME, &dips.diph );                    
  693.         DXUtil_ConvertWideStringToGeneric( strPlayerName, dips.wsz );                    
  694.  
  695.         if( lstrcmp( strPlayerName, TEXT("") ) != 0 )
  696.         {
  697.             m_pDeviceList[iDevice].pPlayerInfo = LookupPlayer( strPlayerName );
  698.             if( m_pDeviceList[iDevice].pPlayerInfo )
  699.                 m_pDeviceList[iDevice].pPlayerInfo->bFoundDeviceForPlayer = TRUE;
  700.         }
  701.     }
  702.  
  703.     return S_OK;
  704. }
  705.  
  706.  
  707.  
  708.  
  709. //-----------------------------------------------------------------------------
  710. // Name: UnacquireDevices
  711. // Desc:
  712. //-----------------------------------------------------------------------------
  713. VOID CMultiplayerInputDeviceManager::UnacquireDevices()
  714. {
  715.     // Unacquire every device
  716.  
  717.     if( m_pDeviceList )
  718.     {
  719.         // All devices have been assigned a to a user in 
  720.         // the new array, so clean up the local array
  721.         for( DWORD iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  722.         {
  723.             LPDIRECTINPUTDEVICE8 pdidDevice = m_pDeviceList[iDevice].pdidDevice;
  724.             
  725.             // Set the device's coop level
  726.             pdidDevice->Unacquire();
  727.         }
  728.     }
  729. }
  730.  
  731.  
  732.  
  733.  
  734. //-----------------------------------------------------------------------------
  735. // Name: SetFocus
  736. // Desc: Sets the DirectInput focus to a new HWND
  737. //-----------------------------------------------------------------------------
  738. VOID CMultiplayerInputDeviceManager::SetFocus( HWND hWnd ) 
  739. {
  740.     m_hWnd = hWnd;
  741.     
  742.     if( m_pDeviceList )
  743.     {
  744.         // All devices have been assigned a to a user in 
  745.         // the new array, so clean up the local array
  746.         for( DWORD iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  747.         {
  748.             LPDIRECTINPUTDEVICE8 pdidDevice = m_pDeviceList[iDevice].pdidDevice;
  749.  
  750.             // Set the device's coop level
  751.             pdidDevice->Unacquire();
  752.             pdidDevice->SetCooperativeLevel( m_hWnd, DISCL_NONEXCLUSIVE|DISCL_FOREGROUND );
  753.         }
  754.     }
  755. }
  756.  
  757.  
  758.  
  759.  
  760. //-----------------------------------------------------------------------------
  761. // Name: GetDevices
  762. // Desc: returns an array of DeviceInfo*'s
  763. //-----------------------------------------------------------------------------
  764. HRESULT CMultiplayerInputDeviceManager::GetDevices( DeviceInfo** ppDeviceInfo, 
  765.                                          DWORD* pdwCount )
  766. {
  767.     if( NULL==ppDeviceInfo || NULL==pdwCount )
  768.         return E_INVALIDARG;
  769.     
  770.     (*ppDeviceInfo) = m_pDeviceList;
  771.     (*pdwCount)     = m_dwNumDevices;
  772.     
  773.     return S_OK;
  774. }
  775.  
  776.  
  777.  
  778.  
  779. //-----------------------------------------------------------------------------
  780. // Name: LookupPlayer
  781. // Desc: searchs m_pUsers by player name
  782. //-----------------------------------------------------------------------------
  783. CMultiplayerInputDeviceManager::PlayerInfo* CMultiplayerInputDeviceManager::LookupPlayer( TCHAR* strPlayerName )
  784. {
  785.     for( DWORD iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
  786.     {
  787.         PlayerInfo* pCurPlayer = m_pUsers[iPlayer];
  788.         if( lstrcmp( pCurPlayer->strPlayerName, strPlayerName ) == 0 )
  789.             return pCurPlayer;
  790.     }
  791.     
  792.     return NULL;
  793. }
  794.  
  795.  
  796.  
  797.  
  798. //-----------------------------------------------------------------------------
  799. // Name: SaveDeviceOwnershipKeys
  800. // Desc: For every device that's assigned to a player, save its device key 
  801. //       and assigned player to registry.
  802. //-----------------------------------------------------------------------------
  803. HRESULT CMultiplayerInputDeviceManager::SaveDeviceOwnershipKeys()
  804. {
  805.     TCHAR strDeviceGuid[40];
  806.     DWORD iDevice;
  807.  
  808.     for( iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  809.     {                
  810.         PlayerInfo* pPlayerInfo = m_pDeviceList[iDevice].pPlayerInfo;           
  811.  
  812.         DXUtil_ConvertGUIDToString( &m_pDeviceList[iDevice].didi.guidInstance, strDeviceGuid );
  813.  
  814.         if( pPlayerInfo != NULL )
  815.             DXUtil_WriteStringRegKey( m_hKey, strDeviceGuid, pPlayerInfo->strPlayerName );        
  816.         else
  817.             RegDeleteValue( m_hKey, strDeviceGuid );
  818.     }                              
  819.  
  820.     return S_OK;
  821. }
  822.  
  823.  
  824.  
  825.  
  826. //-----------------------------------------------------------------------------
  827. // Name: DeleteDeviceOwnershipKeys
  828. // Desc: Delete all the ownership keys
  829. //-----------------------------------------------------------------------------
  830. VOID CMultiplayerInputDeviceManager::DeleteDeviceOwnershipKeys()
  831. {
  832.     HKEY hKey;
  833.     TCHAR *strRegKey;
  834.  
  835.     // Prepare strings to delete the key
  836.     strRegKey = _tcsdup( m_strKey );
  837.     if( strRegKey == NULL )
  838.         return;
  839.  
  840.     TCHAR* strTemp = _tcsrchr( strRegKey, TEXT('\\') );
  841.     
  842.     // Unless the registry path string was malformed, we're ready to delete
  843.     // and recreate the key
  844.     if( strTemp ) 
  845.     {
  846.         *strTemp = 0;
  847.         strTemp++;
  848.  
  849.         RegCloseKey( m_hKey );
  850.  
  851.         // Delete the reg key
  852.         RegOpenKey( HKEY_CURRENT_USER, strRegKey, &hKey );
  853.         SHDeleteKey( hKey, strTemp );
  854.         RegCloseKey( hKey );
  855.     
  856.         // Create the key again now that all the subkeys have been deleted
  857.         RegCreateKeyEx( HKEY_CURRENT_USER, m_strKey, 0, NULL,
  858.                         REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, 
  859.                         &m_hKey, NULL );
  860.     }
  861.  
  862.  
  863.     // Clean up memory allocation
  864.     free( strRegKey );
  865. }
  866.  
  867.  
  868.  
  869.  
  870. //-----------------------------------------------------------------------------
  871. // Name: Cleanup
  872. // Desc:
  873. //-----------------------------------------------------------------------------
  874. VOID CMultiplayerInputDeviceManager::Cleanup()
  875. {
  876.     CleanupDeviceList();
  877.     
  878.     if( m_pUsers )
  879.     {
  880.         for( DWORD iPlayer=0; iPlayer<m_dwNumUsers; iPlayer++ )
  881.             SAFE_DELETE( m_pUsers[iPlayer] );
  882.         SAFE_DELETE( m_pUsers );
  883.     }
  884.     
  885.     // Release() base object
  886.     SAFE_RELEASE( m_pDI );
  887.     
  888. }
  889.     
  890.  
  891.  
  892.  
  893. //-----------------------------------------------------------------------------
  894. // Name: CleanupDeviceList
  895. // Desc: Clean up the device array
  896. //-----------------------------------------------------------------------------
  897. VOID CMultiplayerInputDeviceManager::CleanupDeviceList()
  898. {
  899.     for( DWORD iDevice=0; iDevice<m_dwNumDevices; iDevice++ )
  900.         SAFE_RELEASE( m_pDeviceList[iDevice].pdidDevice );
  901.     free( m_pDeviceList );
  902.     m_pDeviceList = NULL;
  903.     m_dwMaxDevices = 0;
  904.     m_dwNumDevices = 0;    
  905. }
  906.  
  907.  
  908.  
  909.  
  910.